#include "Drive.h"
#include <glad/glad.h>
#include <SERender/Util/Util.h>

namespace SEngine
{
    int  Drive<OpenGL>::Load()
    {
        return !gladLoadGL();
    }

    int  Drive<OpenGL>::Load(void* addr) {
        return !gladLoadGLLoader((GLADloadproc)(addr));
    }

    void Drive<OpenGL>::Viewport(int x, int y, int w, int h) {
        glViewport(x, y, w, h);
    }

    void Drive<OpenGL>::Clear(int type) {
        glClear(type);
    }

    void Drive<OpenGL>::DrawArrays(ElemMode mode, int first, size_t count, size_t instanceCount) 
    {
        if (instanceCount > 1) {
            glDrawArraysInstanced(mode, first, (GLsizei)count, (GLsizei)instanceCount);
        } else {
            glDrawArrays(mode, first, (GLsizei)count);
        }
    }

    void Drive<OpenGL>::DrawElements(ElemMode mode, int size, ElemType type, void* indices, size_t instanceCount)
    {
        if (instanceCount > 1) {
            glCall(glDrawElementsInstanced(mode, size, type, indices, (GLsizei)instanceCount));
        } else {
            glCall(glDrawElements(mode, size, type, indices));
        }
    }

    void Drive<OpenGL>::ClearColor(float r, float g, float b, float a)
    {
        glClearColor(r, g, b, a);
    }

    void Drive<OpenGL>::SetLineWidth(float width)
    {
        glLineWidth(width);
    }

    void Drive<OpenGL>::BufferData(int target, size_t size, const void * data, int usage) 
    {
        glCall(glBufferData(target, size, data, usage));
    }

    void Drive<OpenGL>::VertexAttribPointer(uint32_t index, int size, int type, bool normalized, int stride, const void *pointer) 
    {
        glCall(glVertexAttribPointer(index, size, type, normalized, stride, pointer));
    }

    void Drive<OpenGL>::EnableVertexAttribArray(uint32_t index) 
    {
        glCall(glEnableVertexAttribArray(index));
    }

    void Drive<OpenGL>::BufferSubData(int target, signed long long int offset, size_t size, const void *data)
    {
        glCall(glBufferSubData(target, offset, size, data));
    }

    void Drive<OpenGL>::SetPipeLineState(PipeLineState pso) 
    {
        if (pso.blend) {
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        } else {
            glDisable(GL_BLEND);
        }
        if (pso.cullFace) {
            glEnable(GL_CULL_FACE);
            pso.cullFront ? glCullFace(GL_FRONT) : glCullFace(GL_BACK);
        } else {
            glDisable(GL_CULL_FACE);
        }
        pso.depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
    }

    void Drive<OpenGL>::ReadPixels(int x, int y, int width, int height, ColorFormat format, ElemType type, void *pixels) 
    {
        glCall(glReadPixels(x, y, width, height, format, type, pixels));
    }

    void Drive<OpenGL>::EnableByteAlignment(bool value)
    {
        glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)!value);
    }

    void Drive<OpenGL>::EnableMultiSample(bool value) 
    {
        if (value) {
            glEnable(GL_MULTISAMPLE);
        } else {
            glDisable(GL_MULTISAMPLE);
        }
    }

    void Drive<OpenGL>::SetPolygonMode(GraphicsFace face, GraphicsFaceMode mode) 
    {
        glCall(glPolygonMode(face, mode));
    }

    void Drive<OpenGL>::SetStencilFunc(GraphicsEnum en, int ref, uint32_t mask) 
    {
        glCall(glStencilFunc(en, ref, mask));
    }

    void Drive<OpenGL>::SetStencilOp(GraphicsEnum sfail, GraphicsEnum dpfail, GraphicsEnum dppass) 
    {
        glCall(glStencilOp(sfail, dpfail, dppass));
    }

    void Drive<OpenGL>::SetStencilMask(uint32_t mask)
    {
        glCall(glStencilMask(mask));
    }

    void Drive<OpenGL>::SetEnable(GraphicsEnum en)
    {
        glEnable(en);
    }

    void Drive<OpenGL>::SetDisable(GraphicsEnum en) 
    {
        glDisable(en);
    }

    void Drive<OpenGL>::SetClearStencil(int s)
    {
        glClearStencil(s);
    }

    void Drive<OpenGL>::SetColorMask(bool r, bool g, bool b, bool a) 
    {
        glColorMask(r, g, b, a);
    }

    void Drive<OpenGL>::DispatchCompute(uint32_t x, uint32_t y, uint32_t z) 
    {
        glCall(glDispatchCompute(x, y, z));
        glMemoryBarrier(GL_ALL_BARRIER_BITS);
    } 


} // namespace SEngine



